import reducers from './reducers/gameReducer'
import { Level } from './types/levelsTypes'
import * as actions from './actions/gameActions'
import { CellCoordinate, GameState } from './types/gameTypes'
import GameModal from './components/GameModal'

const { ccclass, property } = cc._decorator
var store = null

@ccclass
export default class GameScene extends cc.Component {

  @property(cc.Label)
  levelTitle: cc.Label = null

  @property(cc.Node)
  gameContainer = null

  @property(cc.Prefab)
  placeholder = null

  @property(cc.Prefab)
  modalDialog = null

  @property(cc.Sprite)
  car = null

  @property(cc.Sprite)
  fire = null

  levelData: Level = null

  modal: cc.Node = null

  @property({ type: cc.AudioClip })
  successAudio = null
  @property({ type: cc.AudioClip })
  failureAudio = null

  bgAudioID: number

  shuffleArr(arr: Array<string>) {    // Knuth-Durstenfeld shuffle ES6
    let n = arr.length, random: number
    while (0 != n) {
        random = (Math.random() * n--) >>> 0;         // 无符号右移位运算符向下取整
        [arr[n], arr[random]] = [arr[random], arr[n]] // ES6的结构赋值实现变量互换
    }
    return arr;
}

  back() {
    cc.audioEngine.stop(this.bgAudioID)
    cc.director.loadScene('Levels')
  }

  initGameMap() {        // 转化原始数据并初始化游戏元素
    const cellWidth = 90    // 元素的大小
    const startCoor = new CellCoordinate(this.levelData.startPoint)  // 开始坐标
    const endCoor = new CellCoordinate(this.levelData.endPoint)
    let hurdles = new Array()   // 占位
    for (var i = 0, len = this.levelData.hurdles.length; i < len - 1; i = i + 1) {
      const dataArr = this.levelData.hurdles[i].split(',')
      hurdles.push(cc.v2(parseInt(dataArr[2]), parseInt(dataArr[1])))
    }
    // 正确答案列表 ['T_right', 'T_right']
    const answer = this.levelData.hurdles[this.levelData.hurdles.length - 1].split(',').slice(1, this.levelData.hurdles[i].length - 1)
    // 根据答案随机生成输入列表
    const inputs = this.shuffleArr(answer.slice())

    // 输入项的起坐标
    const inputSpacing = 20
    const inputsWidth = inputs.length * (cellWidth + inputSpacing) - inputSpacing
    const inputStartX = (this.gameContainer.width - inputsWidth) / 2
    const inputStartY = -380

    // 初始化地图占位
    hurdles.forEach(obj => {
      let placeholder = cc.instantiate(this.placeholder)
      placeholder.x = obj.x * cellWidth
      placeholder.y = -obj.y * cellWidth
      this.gameContainer.addChild(placeholder)
    })
    // 起点
    this.car.node.x = startCoor.x * cellWidth
    this.car.node.y = -startCoor.y * cellWidth
    // 终点
    this.fire.node.x = endCoor.x * cellWidth
    this.fire.node.y = -endCoor.y * cellWidth

    // 初始化输入项
    inputs.forEach((obj: string, idx: number) => {
      let node = new cc.Node('inputItem_' + idx)
      node.anchorX = 0; node.anchorY = 1
      node.width = cellWidth; node.height = cellWidth
      let sprite = node.addComponent(cc.Sprite)
      sprite.sizeMode = cc.Sprite.SizeMode.CUSTOM
      cc.loader.loadRes(obj, cc.SpriteFrame, function (err, spriteFrame) {
        sprite.spriteFrame = spriteFrame
      })
      this.gameContainer.addChild(node)
      node.on(cc.Node.EventType.TOUCH_START, () => {
        store.dispatch(actions.gameInputStep(idx))
      })
    })

    this.car.node.zIndex = 99
    // this.car.node.setSiblingIndex(99)

    // 游戏开始
    store.dispatch(actions.gameStart({
      cellWidth: cellWidth,
      finished: false,
      currentInputIdx: 0,
      selectInputIdxs: [],
      startCoor: startCoor,
      endCoor: endCoor,
      hurdles: hurdles,
      answer: answer,
      inputs: inputs,
      inputSpacing: inputSpacing,
      inputStartVec2: cc.v2(inputStartX, inputStartY),
      playing: false
    }))

    this.playBackgroundAudio()
  }

  playBackgroundAudio() {   // 背景音乐异步加载
    cc.loader.loadRes('audio/idle', cc.AudioClip, (err, clip) => {
      this.bgAudioID = cc.audioEngine.playMusic(clip, true)
    })
  }

  reloadCarPosition() {
    let gameState = store.getState() as GameState
    const descX = gameState.startCoor.x * gameState.cellWidth
    const descY = -gameState.startCoor.y * gameState.cellWidth
    if (this.car.node.x == descX && this.car.node.y == descY) {
      return
    }
    cc.tween(this.car.node).to(0.3, { position: cc.v2(descX, descY) }).start()
  }
  // 每个input的位置
  reloadPositionsForInputItem(animated: boolean, inputPositions: Array<cc.Vec2>) {
    inputPositions.forEach((obj: cc.Vec2, idx: number) => {
      let inputItem = this.gameContainer.getChildByName(`inputItem_${idx}`)
      if (inputItem) {
        if (inputItem.x == obj.x && inputItem.y == obj.y) {     // 位置相同无须调整
          return
        }
        if (animated) {
          cc.tween(inputItem).to(0.3, { position: cc.v2(obj.x, obj.y) }).start()
        } else {
          inputItem.x = obj.x
          inputItem.y = obj.y
        }
      }
    })
  }

  nextStepForCar(step: number, currentInputItem: string) {		// 车辆下一步的行为
    let gameState = store.getState() as GameState
		var nextCoor = {
			x: gameState.hurdles[step].x,
			y: gameState.hurdles[step].y
		}
		const nextDirect = currentInputItem.split('_').pop()
		if (nextDirect == 'forward') {
			nextCoor.y = nextCoor.y - 1
			} else if (nextDirect == 'back') {
			nextCoor.y = nextCoor.y + 1
			} else if (nextDirect == 'left') {
			nextCoor.x = nextCoor.x - 1
			} else if (nextDirect == 'right') {
			nextCoor.x = nextCoor.x + 1
    }
    cc.tween(this.car.node).to(0.3, {
      position: cc.v2(nextCoor.x * gameState.cellWidth, -nextCoor.y * gameState.cellWidth)
    }).start()
	}
	
  checkAnswer() {
    let gameState = store.getState() as GameState
    // 第一步直接移动到图像第一个节点
    cc.tween(this.car.node).to(0.3, {
      position: cc.v2(gameState.hurdles[0].x * gameState.cellWidth, -gameState.hurdles[0].y * gameState.cellWidth) 
    }).call(() => {
			var timeId = setInterval(gotoNextStep.bind(this), 300)
			var step = 0
			function gotoNextStep() {
				if (step < gameState.hurdles.length) {  // 根据答案生成坐标
					const currentInputItemIdx = gameState.selectInputIdxs[step]
					const currentInputItem = gameState.inputs[currentInputItemIdx]
					if (currentInputItem != gameState.answer[step]) { // 答案错误
            clearInterval(timeId)
            cc.audioEngine.playEffect(this.failureAudio, false)
            this.showFailureModal()
						return
					}
					this.nextStepForCar(step, currentInputItem)       // 目前答案正确，继续移动
				}
				step += 1
				if (step >= gameState.hurdles.length - 1) {         // 成功
					clearInterval(timeId)
					setTimeout(() => {
            cc.audioEngine.playEffect(this.successAudio, false)
            this.showSuccessModal()
					}, 800)
				}
			}
    }).start()
  }

  showSuccessModal() {
    if (!this.modal) {
      this.modal = cc.instantiate(this.modalDialog)
    }
    const dataNode = this.getLevelDataNode()
    const lastLevel = dataNode.levels.length - 1 == dataNode.current
    let md = this.modal.getComponent('GameModal') as GameModal
    md.titleLabel.string = '恭喜'
    md.contentLabel.string = '救火成功！' + lastLevel ? '已通关！' : ''
    md.buttonLabel1.string = '再玩一次'
    md.buttonLabel2.string = lastLevel ? '返回' : '下一关'
    md.button1.node.off('click')
    md.button1.node.on('click', () => { // 再玩一次
      store.dispatch(actions.gameRestart())
      this.modal.active = false
    })
    md.button2.node.off('click')
    md.button2.node.on('click', () => { // 返回/下一关
      if (lastLevel) {
        this.back()
      } else {
        dataNode.current += 1
        cc.director.loadScene('Game')
        this.modal.active = false
      }
    })
    this.modal.active = true
    this.modal.parent = this.node
  }
  showFailureModal() {
    if (!this.modal) {
      this.modal = cc.instantiate(this.modalDialog)
    }
    let md = this.modal.getComponent('GameModal') as GameModal
    md.titleLabel.string = '很遗憾'
    md.contentLabel.string = '救火失败！'
    md.buttonLabel1.string = '返回'
    md.buttonLabel2.string = '再试一次'
    md.button1.node.off('click')
    md.button1.node.on('click', () => { // 返回
      this.back()
    })
    md.button2.node.off('click')
    md.button2.node.on('click', () => { // 再试一次
      store.dispatch(actions.gameRestart())
      this.modal.active = false
    })
    this.modal.active = true
    this.modal.parent = this.node
  }

  checkAnswerIfGameFinish(gameState) {
    if (gameState.finished) {
      setTimeout(this.checkAnswer.bind(this), 500)
    } else {
      if (gameState.selectInputIdxs.length == gameState.answer.length) {
        store.dispatch(actions.gameFinishAnswer())
      }
    }
  }

  handleGameStateChange() {
    let gameState = store.getState() as GameState
    this.reloadCarPosition()
    this.reloadPositionsForInputItem(gameState.playing, gameState.inputPositions)
    this.checkAnswerIfGameFinish(gameState)
  }

  getLevelDataNode() {
    return cc.director.getScene().getChildByName('dataNode').getComponent('DataNode')
  }

  // LIFE-CYCLE CALLBACKS:
  onLoad() {
    store = Redux.createStore(reducers)
    store.subscribe(() => {     // 绑定UI
      this.handleGameStateChange()
    })

    const dataNode = this.getLevelDataNode()
    this.levelData = dataNode.levels[dataNode.current] as Level

    this.levelTitle.string = `Level ${this.levelData.no}`
    this.initGameMap()
  }

  // start () {}

  // update (dt) {}

  onDestroy () {
    cc.audioEngine.stopAllEffects()
  }
}
